import { DefaultStage } from "../default-stage";
import { Global } from '../../data/data';
import { HUB_SCOPE, MessageHub } from "../../../../common/global/message-hub";
import { ISetupInfo, OUTFIT_TYPE, USER_HUB_MESSAGE_TYPE } from "../../../../common/global/user-hub-message-type";
import { GLTFMeshTaskInfo } from "babylon-lib/index";
import { CombineRig } from "../../../../common/skinned-animate/combine-rig";
import { AssetManager } from "../../../../common/assets/asset-manager";
import { Helper } from "babylon-lib/tool/helper";


class InitializeHub {

    private static _combineRig: CombineRig;

    public static async initialize(stage: DefaultStage): Promise<void> {
        Global.hub = new MessageHub<USER_HUB_MESSAGE_TYPE>(window.parent, HUB_SCOPE.USER);
        this.setup(stage);
        this.bindSelectEvent(stage);
        this.bindSwitchEvent(stage);

        Global.hub.dispatch(USER_HUB_MESSAGE_TYPE.IFRAME_READY, undefined);
    }

    private static bindSwitchEvent(stage: DefaultStage): void {
        Global.hub!.listen<{ type: string, url: string }[]>(USER_HUB_MESSAGE_TYPE.SWITCH_CHARACTER, async (e) => {
            console.log('3D switch character', e.data);
            let characterUrl = '';
            e.data.forEach(element => {
                if (element.type === OUTFIT_TYPE.CHARACTER) characterUrl = element.url;
            });
            if (Global.info!.characterUrl === characterUrl) return;

            [Global.info!.characterUrl, Global.info!.chestUrl, Global.info!.headUrl, Global.info!.legUrl, Global.info!.footUrl, Global.info!.ornamentUrl].forEach((element) => {
                if (element) {
                    const old = AssetManager.getAsset(element) as GLTFMeshTaskInfo;
                    this._combineRig.removeSkeleton(old.loadedSkeletons![0]);
                    old.loadedMeshes!.forEach(m => Helper.getChildMeshes(m).forEach(m => m.isVisible = false));
                }
            });
            for (const element of e.data) {
                const ast = await AssetManager.fetch(element.url, stage.loader(stage.mainScene)) as GLTFMeshTaskInfo;
                let isAction = false;
                switch (element.type) {
                    case OUTFIT_TYPE.CHARACTER:
                        Global.info!.characterUrl = element.url;
                        break;
                    case OUTFIT_TYPE.CHEST:
                        Global.info!.chestUrl = element.url;
                        break;
                    case OUTFIT_TYPE.HEAD:
                        Global.info!.headUrl = element.url;
                        break;
                    case OUTFIT_TYPE.LEG:
                        Global.info!.legUrl = element.url;
                        break;
                    case OUTFIT_TYPE.FOOT:
                        Global.info!.footUrl = element.url;
                        break;
                    case OUTFIT_TYPE.ORNAMENT:
                        Global.info!.ornamentUrl = element.url;
                        break;
                    case OUTFIT_TYPE.IDLE:
                        Global.info!.idleUrl = element.url;
                        isAction = true;
                        break;
                    case OUTFIT_TYPE.WALK:
                        Global.info!.walkUrl = element.url;
                        isAction = true;
                        break;
                    case OUTFIT_TYPE.RUN:
                        Global.info!.runUrl = element.url;
                        isAction = true;
                        break;
                    case OUTFIT_TYPE.JUMP:
                        Global.info!.jumpUrl = element.url;
                        isAction = true;
                        break;
                }
                if (!isAction) {
                    this._combineRig.addSkeleton(...ast.loadedSkeletons || []);
                    ast.loadedMeshes!.forEach(m => {
                        m.isVisible = false;
                        m.alwaysSelectAsActiveMesh = true;
                    });
                    setTimeout(() => {
                        ast.loadedMeshes!.forEach(m => m.isVisible = true);
                    }, 100);
                } else if (element.type === OUTFIT_TYPE.IDLE) {
                    this._combineRig.setAnimationGroup((ast as GLTFMeshTaskInfo).loadedAnimationGroups![0]!);
                    this._combineRig.play(true);
                }
            }
        });

        Global.hub!.listen<{ type: string, url: string }>(USER_HUB_MESSAGE_TYPE.SWITCH, async (e) => {
            console.log('3D switch component', e.data);

            const updateAction = async (oldUrl: string, newUrl: string): Promise<string | null> => {
                if (oldUrl === newUrl) return null;
                let ast = await AssetManager.fetch(newUrl, stage.loader(stage.mainScene));
                this._combineRig.setAnimationGroup((ast as GLTFMeshTaskInfo).loadedAnimationGroups![0]!);
                this._combineRig.play(true);
                return newUrl;
            };
            const updateModel = async (oldUrl: string, newUrl: string): Promise<string | null> => {
                if (oldUrl === newUrl) return null;
                let old = (AssetManager.getAsset(oldUrl) as GLTFMeshTaskInfo);
                let ast = await AssetManager.fetch(newUrl, stage.loader(stage.mainScene)) as GLTFMeshTaskInfo;
                this._combineRig.removeSkeleton(old.loadedSkeletons![0]);
                this._combineRig.addSkeleton(ast.loadedSkeletons![0]);
                old.loadedMeshes!.forEach(m => m.isVisible = false);
                ast.loadedMeshes!.forEach(m => {
                    m.isVisible = true;
                    m.alwaysSelectAsActiveMesh = true;
                });
                return newUrl;
            };

            let ast = null;
            switch (e.data.type) {
                case OUTFIT_TYPE.IDLE:
                    Global.info!.idleUrl = await updateAction(Global.info!.idleUrl, e.data.url) || Global.info!.idleUrl;
                    break;
                case OUTFIT_TYPE.WALK:
                    Global.info!.walkUrl = await updateAction(Global.info!.walkUrl, e.data.url) || Global.info!.walkUrl;
                    break;
                case OUTFIT_TYPE.RUN:
                    Global.info!.runUrl = await updateAction(Global.info!.runUrl, e.data.url) || Global.info!.runUrl;
                    break;
                case OUTFIT_TYPE.JUMP:
                    Global.info!.jumpUrl = await updateAction(Global.info!.jumpUrl, e.data.url) || Global.info!.jumpUrl;
                    break;
                case OUTFIT_TYPE.CHEST:
                    Global.info!.chestUrl = await updateModel(Global.info!.chestUrl, e.data.url) || Global.info!.chestUrl;
                    break;
                case OUTFIT_TYPE.HEAD:
                    Global.info!.headUrl = await updateModel(Global.info!.headUrl, e.data.url) || Global.info!.headUrl;
                    break;
                case OUTFIT_TYPE.LEG:
                    Global.info!.legUrl = await updateModel(Global.info!.legUrl, e.data.url) || Global.info!.legUrl;
                    break;
                case OUTFIT_TYPE.FOOT:
                    Global.info!.footUrl = await updateModel(Global.info!.footUrl, e.data.url) || Global.info!.footUrl;
                    break;
                case OUTFIT_TYPE.ORNAMENT:
                    Global.info!.ornamentUrl = await updateModel(Global.info!.ornamentUrl, e.data.url) || Global.info!.ornamentUrl;
                    break;
            }
        });
    }

    private static bindSelectEvent(stage: DefaultStage): void {
        Global.hub!.listen<{ type: string }>(USER_HUB_MESSAGE_TYPE.SELECT, async (e) => {
            // console.log('3D select', e.data,Global.info);
            let url;
            switch (e.data.type) {
                case OUTFIT_TYPE.IDLE:
                    url = Global.info!.idleUrl!;
                    break;
                case OUTFIT_TYPE.WALK:
                    url = Global.info!.walkUrl!;
                    break;
                case OUTFIT_TYPE.RUN:
                    url = Global.info!.runUrl!;
                    break;
                case OUTFIT_TYPE.JUMP:
                    url = Global.info!.jumpUrl!;
                    break;
            }
            if (!url) return;
            const ast = await AssetManager.fetch(url, stage.loader(stage.mainScene));
            const ag = (ast as GLTFMeshTaskInfo).loadedAnimationGroups![0]!;
            this._combineRig.setAnimationGroup(ag);
            this._combineRig.play(true);
        });
    }

    private static setup(stage: DefaultStage): void {
        Global.hub!.listen<ISetupInfo>(USER_HUB_MESSAGE_TYPE.SETUP, async (e) => {
            console.log('3d', e);
            const data = e.data;
            Global.info = data;
            const urls: string[] = [];
            for (const key in data) {
                const url = (data as any)[key] as string;
                if (url) {
                    urls.push(url);
                }
            }

            const loader = stage.loader(stage.mainScene);
            await AssetManager.load(urls, loader);

            const cr = new CombineRig('test', {
                frameLimit: 20,
                animationGroup: loader.getMeshTaskInfo(data.idleUrl!)!.loadedAnimationGroups![0]!,
                skeletons: [
                    loader.getMeshTaskInfo(data.characterUrl!)!.loadedSkeletons![0]!,
                    loader.getMeshTaskInfo(data.chestUrl!)!.loadedSkeletons![0]!,
                    loader.getMeshTaskInfo(data.footUrl!)!.loadedSkeletons![0]!,
                    loader.getMeshTaskInfo(data.headUrl!)!.loadedSkeletons![0]!,
                    loader.getMeshTaskInfo(data.legUrl!)!.loadedSkeletons![0]!,
                ],
            });
            cr.play(true);
            this._combineRig = cr;
        })
    }

}


export { InitializeHub };